Maven 总结 Apache Maven,是一个软件(特别是Java软件)项目管理及自动构建工具 ,由Apache软件基金会所提供。基于项目对象模型(Project Object Model缩写:POM) 概念,Maven利用一个中央信息片断能管理一个项目的构建、报告和文档等步骤。Maven也可被用于构建和管理各种项目,例如C#,Ruby,Scala和其他语言编写的
入门 仓库 在 Maven 的术语中,仓库是一个位置(place)。Maven 仓库是项目中依赖的第三方库,这个库所在的位置叫做仓库。在 Maven 中,任何一个依赖、插件或者项目构建的输出,都可以称之为构件。
Maven 仓库能帮助我们管理构件(主要是JAR),它就是放置所有JAR文件(WAR,ZIP,POM等等)的地方。Maven 仓库有三种类型:
本地(local)
中央(central)
远程(remote)
Maven 的本地仓库,在安装 Maven 后并不会创建,它是在第一次执行 maven 命令的时候才被创建。运行 Maven 的时候,Maven 所需要的任何构件都是直接从本地仓库获取的。如果本地仓库没有,它会首先尝试从远程仓库下载构件至本地仓库,然后再使用本地仓库的构件。默认情况下,不管Linux还是 Windows,每个用户在自己的用户目录下都有一个路径名为 .m2/respository/ 的仓库目录。
Maven 中央仓库是由 Maven 社区提供的仓库,其中包含了大量常用的库。中央仓库包含了绝大多数流行的开源Java构件,以及源码、作者信息、SCM、信息、许可证信息等。一般来说,简单的Java项目依赖的构件都可以在这里下载到。中央仓库的关键概念:1这个仓库由 Maven 社区管理。2不需要配置。3需要通过网络才能访问。要浏览中央仓库的内容,maven 社区提供了一个 URL:http://search.maven.org/#browse 。使用这个仓库,开发人员可以搜索所有可以获取的代码库。
如果 Maven 在中央仓库中也找不到依赖的文件,它会停止构建过程并输出错误信息到控制台。为避免这种情况,Maven 提供了远程仓库的概念,它是开发人员自己定制仓库,包含了所需要的代码库或者其他工程中用到的 jar 文件。举例说明,使用下面的 pom.xml,Maven 将从远程仓库中下载该 pom.xml 中声明的所依赖的(在中央仓库中获取不到的)文件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 <project xmlns ="http://maven.apache.org/POM/4.0.0" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" > <modelVersion > 4.0.0</modelVersion > <groupId > com.companyname.projectgroup</groupId > <artifactId > project</artifactId > <version > 1.0</version > <dependencies > <dependency > <groupId > com.companyname.common-lib</groupId > <artifactId > common-lib</artifactId > <version > 1.0.0</version > </dependency > <dependencies > <repositories > <repository > <id > companyname.lib1</id > <url > http://download.companyname.org/maven2/lib1</url > </repository > <repository > <id > companyname.lib2</id > <url > http://download.companyname.org/maven2/lib2</url > </repository > </repositories > </project >
高级设置
每个公司或者和小组织一般都有自己的私有仓库,所以加入团队要首先配置自己的settings.xml文件, 当然最直接的方式是直接从同事那里进行拷贝。
接下来是配置本地仓库位置,默认位置是${user.home}/.m2/repository/ , 如果想将仓库位置改成自己想要的位置,在settings.xml中修改localRepository的属性就可以
1 2 3 4 5 <settings > ... <localRepository > /path/to/local/repo/</localRepository > ... </settings >
配置私有仓库和仓库的注册用户名、密码。公司的远程私有仓库部署定义在一个项目中的pom.xml文件中,通过<distributionManagement>
来定义发布仓库位置,有几个仓库就在里面定义几个<repository>
标签,每个仓库都有唯一的<id>
标签和<url>
标签。公司的私有仓库一般需要用户名,密码去认证才能进行下载,但是这些不能保存在项目中,pom文件是要上传到git服务器上,所有人都能看到,不安全,基于这个考虑可以在setting.xml进行添加,通过定义<servers>
标签来定义多个私有仓库认证信息,每个仓库都有一个<server>
相对应,然后通过定义<id>
标签与pom文件中的<repository>
中的<id>
标签相同来对应,然后在里面定义<username>
标签和<password>
标签。如下分别是pom.xml中定义仓库位置,setting.xml定义对应的私有仓库用户名和密码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 pom.xml <project > .... <distributionManagement > <repository > <id > libs-releases</id > <url > http://mvn.hz.netease.com/artifactory/libs-releases</url > </repository > <snapshotRepository > <id > libs-snapshots</id > <url > http://mvn.hz.netease.com/artifactory/libs-snapshots</url > </snapshotRepository > </distributionManagement > </project >
1 2 3 4 5 6 7 8 9 10 11 12 13 setting.xml <servers > <server > <id > libs-snapshots</id > <username > ******</username > <password > ******</password > </server > <server > <id > libs-releases</id > <username > ******</username > <password > ******</password > </server > </servers >
可以发现pom中的仓库的id与setting的id是相对应的。
基础pom信息
标识符
含义
groupId
一般用该项目的组织或团体的域名来标识,例如:org.apache.maven.plugins
artifactId
代表唯一的工程名
version
版本号
packaging
标识打包的类型,例如有:jar, war, tar
dependencies
该工程内依赖的其他 jar 包
生命周期 maven的生命周期就是对所有构建过程抽象与统一 ,生命周期包含项目的清理、初始化、编译、测试、打包、集成测试、验证、部署、站点生成等几乎所有的过程。
Maven有三套相互独立的生命周期,请注意这里说的是“三套”,而且“相互独立” ,Maven的生命周期并不是一个整体。这三套生命周期分别是:
CleanLifecycle 在进行真正的构建之前进行一些清理工作。
DefaultLifecycle 构建的核心部分,编译,测试,打包,部署等等。
SiteLifecycle 生成项目报告,站点,发布站点。
每套生命周期都由一组阶段(Phase)组成,我们平时在命令行输入的命令总会对应于一个特定的阶段。maven中所有的执行动作(goal)都需要指明自己在这个过程中的执行位置,然后maven执行的时候,就依照过程的发展依次调用这些goal进行各种处理。这个也是maven的一个基本调度机制。
每套生命周期可以分为多个阶段。
Clean生命周期阶段
完成工作
pre-clean
执行一些需要在clean之前完成的工作
clean
移除所有上一次构建生成的文件
post-clean
执行一些需要在clean之后立刻完成的工作
命令“mvn clean”中的就是代表执行上面的clean阶段,在一个生命周期中,运行某个阶段的时候,它之前的所有阶段都会被运行,也就是说,“mvn clean” 等同于 “mvn pre-clean clean” ,如果我们运行“mvn post-clean” ,那么 “pre-clean”,“clean” 都会被运行 。这是Maven很重要的一个规则,可以大大简化命令行的输入。
Maven最重要就是的Default生命周期,也称构建生命周期,绝大部分工作都发生在这个生命周期中 。
Default生命周期阶段
完成工作
validate
验证项目是否正确,以及所有为了完整构建必要的信息是否可用
compile
编译项目的源代码
test
使用合适的单元测试框架运行测试。这些测试应该不需要代码被打包或发布
package
将编译好的代码打包成可分发的格式,如JAR,WAR,或者EAR
verify
执行所有检查,验证包是有效的,符合质量规范
install
安装包至本地仓库,以备本地的其它项目作为依赖使用
deploy
复制最终的包至远程仓库,共享给其它开发人员和项目(通常和一次正式的发布相关)
Site生命周期阶段
完成工作
pre-site
执行一些需要在生成站点文档之前完成的工作
site
生成项目的站点文档
post-site
执行一些需要在生成站点文档之后完成的工作,并且为部署做准备
site-deploy
将生成的站点文档部署到特定的服务器上
插件 Maven本质上是一个插件框架 ,它的核心并不执行任何具体的构建任务,所有这些任务都交给插件来完成 ,像编译是通过maven-compile-plugin实现的、测试是通过maven-surefire-plugin实现的,maven也内置了很多插件,所以我们在项目进行编译、测试、打包的过程是没有感觉到。
Maven 使用 plugin 来执行实际操作的,在默认情况下,Maven 会绑定以下几个插件来完成基本操作。
plugin
function
life cycle phase
maven-clean-plugin
清理上一次执行创建的目标文件
clean
maven-resources-plugin
处理源资源文件和测试资源文件
resources,testResources
maven-compiler-plugin
编译源文件和测试源文件
compile,testCompile
maven-surefire-plugin
执行测试文件
test
maven-jar-plugin
创建 jar
jar
maven-install-plugin
安装 jar,将创建生成的 jar 拷贝到 .m2/repository 下面
install
maven-deploy-plugin
发布 jar
deploy
Maven官方有两个插件列表,第一个列表的GroupId为org.apache.maven.plugins,这里的插件最为成熟,具体地址为:http://maven.apache.org/plugins/index.html 。第二个列表的GroupId为org.codehaus.mojo,这里的插件没有那么核心,但也有不少十分有用,其地址为:http://mojo.codehaus.org/plugins.html 。
Setting.xml settings.xml是maven的全局配置文件。而pom.xml文件是所在项目的局部配置。
settings.xml文件一般存在于两个位置:全局配置:${M2_HOME}/conf/settings.xml
用户配置: user.home/.m2/settings.xml
前者叫做全局配置,对操作系统的所有使用者生效;后者为用户配置,只对当前操作系统的使用者生效。局部配置优先于全局配置。配置优先级从高到低:pom.xml> user settings > global settings如果这些文件同时存在,在应用配置时,会合并它们的内容,如果有重复的配置,优先级高的配置会覆盖优先级低的。如果全局配置和用户配置都存在,它们的内容将被合并,并且用户范围的settings.xml会覆盖全局的settings.xml。
顶级元素 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <settings xmlns ="http://maven.apache.org/SETTINGS/1.0.0" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://maven.apache.org/SETTINGS/1.0.0 https://maven.apache.org/xsd/settings-1.0.0.xsd" > <localRepository /> <interactiveMode /> <usePluginRegistry /> <offline /> <pluginGroups /> <servers /> <mirrors /> <proxies /> <profiles /> <activeProfiles /> </settings >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <settings xmlns ="http://maven.apache.org/SETTINGS/1.0.0" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://maven.apache.org/SETTINGS/1.0.0 https://maven.apache.org/xsd/settings-1.0.0.xsd" > ... <mirrors > <-- 给定仓库的下载镜像。 -- > <mirror > <-- 该镜像的唯一标识符。id 用来区分不同的mirror 元素。 -- > <id > planetmirror.com</id > <-- 镜像名称 -- > <name > PlanetMirror Australia</name > <-- 该镜像的URL 。构建系统会优先考虑使用该URL ,而非使用默认的服务器URL 。 -- > <url > http://downloads.planetmirror.com/pub/maven2</url > <-- 被镜像的服务器的id 。例如,如果我们要设置了一个Maven 中央仓库(http: //repo.maven.apache.org /maven2 /)的镜像,就需要将该元素设置成central 。这必须和中央仓库的id central 完全一致。 -- > <mirrorOf > central</mirrorOf > </mirror > </mirrors > ... </settings >
Proxies 用来配置不同的代理
Profiles 根据环境参数来调整构建配置的列表
Activation自动触发profile 的条件逻辑
Properties 对应profile的扩展属性列表
Repositories 远程仓库列表,它是maven用来填充构建系统本地仓库所使用的一组远程仓库。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 <repositories > <--包含需要连接到远程仓库的信息 -- > <repository > <--远程仓库唯一标识 -- > <id > codehausSnapshots</id > <--远程仓库名称 -- > <name > Codehaus Snapshots</name > <--如何处理远程仓库里发布版本的下载 -- > <releases > <--true或者false表示该仓库是否为下载某种类型构件(发布版,快照版)开启。 -- > <enabled > false</enabled > <--该元素指定更新发生的频率。Maven会比较本地POM和远程POM的时间戳。这里的选项是:always(一直),daily(默认,每日),interval:X(这里X是以分钟为单位的时间间隔),或者never(从不)。 -- > <updatePolicy > always</updatePolicy > <--当Maven验证构件校验文件失败时该怎么做-ignore(忽略),fail(失败),或者warn(警告)。 -- > <checksumPolicy > warn</checksumPolicy > </releases > <--如何处理远程仓库里快照版本的下载。有了releases和snapshots这两组配置,POM就可以在每个单独的仓库中,为每种类型的构件采取不同的策略。例如,可能有人会决定只为开发目的开启对快照版本下载的支持。参见repositories /repository /releases 元素 -- > <snapshots > <enabled /> <updatePolicy /> <checksumPolicy /> </snapshots > <--远程仓库URL,按protocol: //hostname /path 形式 -- > <url > http://snapshots.maven.codehaus.org/maven2</url > <--用于定位和排序构件的仓库布局类型-可以是default(默认)或者legacy(遗留)。Maven 2 为其仓库提供了一个默认的布局;然而,Maven 1.x 有一种不同的布局。我们可以使用该元素指定布局是default (默认)还是legacy (遗留)。 -- > <layout > default</layout > </repository > </repositories >
mirror(镜像)与repository(仓库)区别 repository就是个仓库。maven里有两种仓库,本地仓库和远程仓库。远程仓库相当于公共的仓库,大家都能看到。本地仓库是你本地的一个山寨版,只有你看的到,主要起缓存作用。当你向仓库请求插件或依赖的时候,会先检查本地仓库里是否有。如果有则直接返回,否则会向远程仓库请求,并做缓存。你也可以把你做的东西上传到本地仓库给你本地自己用,或上传到远程仓库,供大家使用。 internal repository是指在局域网内部搭建的repository,它跟central repository, jboss repository等的区别仅仅在于其URL是一个内部网址。
远程仓库可以在工程的pom.xml文件里指定。如果没指定,默认就会把下面这地方做远程仓库,即默认会到http://repo1.maven.org/maven2这个地方去请求插件和依赖包。
镜像是仓库的镜子,保存了被镜像仓库的所有的内容,主要针对远程仓库而言。配置mirror的目的一般是出于网速考虑。如果仓库X可以提供仓库Y存储的所有内容,那么就可以认为X是Y的一个镜像。换句话说,任何一个可以从仓库Y获得的构件,都能够从它的镜像中获取。举个例子,http://maven.NET.cn/content/groups/public/ 是中央仓库http://repo1.maven.org/maven2/ 在中国的镜像,由于地理位置的因素,该镜像往往能够提供比中央仓库更快的务。
<mirrorOf>
的值为central,表示该配置为中央仓库的镜像,任何对于中央仓库的请求都会转至该镜像,用户也可以使用同样的方法配置其他仓库的镜像。
关于镜像的一个更为常见的用法是结合私服。由于私服可以代理任何外部的公共仓库(包括中央仓库),因此,对于组织内部的Maven用户来说,使用一个私服地址就等于使用了所有需要的外部仓库,这可以将配置集中到私服,从而简化Maven本身的配置。在这种情况下,任何需要的构件都可以从私服获得,私服就是所有仓库的镜像。这时,可以配置这样的一个镜像。
Pom.xml pom.xml主要描述了项目 的maven坐标,依赖关系,开发者需要遵循的规则,缺陷管理系统,组织和licenses,以及其他所有的项目相关因素,是项目级别的配置文件 。Super Pom是Maven的默认POM文件,所有的pom文件都会继承这个super pom。
Pom 最少要求包含以下几点project root , model version groupId artifactId version 。例如
1 2 3 4 5 6 <project > <modelVersion > 4.0.0</modelVersion > <groupId > com.mycompany.app</groupId > <artifactId > my-app</artifactId > <version > 1</version > </project >
子模块的pom会继承父模块的pom文件
如果想要模块的groupId 和/或 version与父模块的相同 ,只需要将groupId和/或version从你的模块中删除定义即可,因为pom.xml允许你模块继承父模块的groupId和version。
例如下面是父亲的pom文件和子模块的pom文件,父亲pom里面定义了modules是其中模块的信息,还定义了packaging是pom类型。 子pom里面定义了父pom的parent 标签。
1 2 3 4 5 6 7 8 9 10 11 <project > <modelVersion > 4.0.0</modelVersion > <groupId > com.mycompany.app</groupId > <artifactId > my-app</artifactId > <version > 1</version > <packaging > pom</packaging > <modules > <module > my-module</module > </modules > </project >
1 2 3 4 5 6 7 8 9 10 <project > <parent > <groupId > com.mycompany.app</groupId > <artifactId > my-app</artifactId > <version > 1</version > <relativePath > ../parent/pom.xml</relativePath > </parent > <modelVersion > 4.0.0</modelVersion > <artifactId > my-module</artifactId > </project >
dependencies与dependencyManagement的区别 在项目顶层的POM文件中,会看到dependencyManagement元素。通过该元素来管理jar包的版本,让子项目中引用一个依赖而不用显示的列出版本号。Maven会沿着父子层次向上走,直到找到一个拥有dependencyManagement元素的项目,然后它就会使用在这个dependencyManagement元素中指定的版本号。
这样做的好处:统一管理项目的版本号,确保应用的各个项目的依赖和版本一致,才能保证测试的和发布的是相同的成果,因此,在顶层pom中定义共同的依赖关系。同时可以避免在每个使用的子项目中都声明一个版本号,这样想升级或者切换到另一个版本时,只需要在父类容器里更新,不需要任何一个子项目的修改;如果某个子项目需要另外一个版本号时,只需要在dependencies中声明一个版本号即可。子类就会使用子类声明的版本号,不继承于父类版本号 。
相对于dependencyManagement,所有生命在dependencies里的依赖都会自动引入,并默认被所有的子项目继承。dependencies即使在子项目中不写该依赖项,那么子项目仍然会从父项目中继承该依赖项(全部继承) dependencyManagement里只是声明依赖,并不实现引入,因此子项目需要显示的声明需要用的依赖。如果不在子项目中声明依赖,是不会从父项目中继承下来的;只有在子项目中写了该依赖项,并且没有指定具体版本,才会从父项目中继承该项,并且version和scope都读取自父pom;另外如果子项目中指定了版本号,那么会使用子项目中指定的jar版本 。
举例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <-- 父模块中的配置 -- > <dependencyManagement > <dependencies > <dependency > <groupId > junit</groupId > <artifactid > junit</artifactId > <version > 4.8.2</version > <scope > test</scope > </dependency > <dependency > <groupId > log4j</groupId > <artifactid > log4j</artifactId > <version > 1.2.16</version > </dependency > </dependencies > </dependencyManagement >
1 2 3 4 5 6 7 8 9 10 11 <-- 子模块中选择继承,如果不声明,不继承;不写version ,scope ,继承父模块dependencyManagement 中;如果自己定义version ,则使用自己的依赖 -- > <dependencies > <dependency > <groupId > junit</groupId > <artifactid > junit</artifactId > </dependency > <dependency > <groupId > log4j</groupId > <artifactid > log4j</artifactId > </dependency > </dependencies >
dependencyManagement继承优化 Maven的继承和Java的继承一样,是无法实现多重继承的,如果10个、20个甚至更多模块继承自同一个模块,那么按照我们之前的做法,这个父模块的dependencyManagement会包含大量的依赖。如果你想把这些依赖分类以更清晰的管理,那就不可能了,import scope依赖能解决这个问题 。可以把dependencyManagement放到单独的专门用来管理依赖的POM中,然后在需要使用依赖的模块中通过import scope依赖,就可以引入dependencyManagement 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <project > <modelVersion > 4.0.0</modelVersion > <groupId > com.juvenxu.sample</groupId > <artifactId > sample-dependency-infrastructure</artifactId > <packaging > pom</packaging > <version > 1.0-SNAPSHOT</version > <dependencyManagement > <dependencies > <dependency > <groupId > junit</groupId > <artifactid > junit</artifactId > <version > 4.8.2</version > <scope > test</scope > </dependency > <dependency > <groupId > log4j</groupId > <artifactid > log4j</artifactId > <version > 1.2.16</version > </dependency > </dependencies > </dependencyManagement > </project >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <dependencyManagement > <dependencies > <dependency > <groupId > com.juvenxu.sample</groupId > <artifactid > sample-dependency-infrastructure</artifactId > <version > 1.0-SNAPSHOT</version > <type > pom</type > <scope > import</scope > </dependency > </dependencies > </dependencyManagement > <dependency > <groupId > junit</groupId > <artifactid > junit</artifactId > </dependency > <dependency > <groupId > log4j</groupId > <artifactid > log4j</artifactId > </dependency >
这样,父模块的POM就会非常干净,由专门的packaging为pom的POM来管理依赖,也契合的面向对象设计中的单一职责原则。此外,我们还能够创建多个这样的依赖管理POM,以更细化的方式管理依赖。这种做法与面向对象设计中使用组合而非继承也有点相似的味道。
基础配置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 <project xmlns ="http://maven.apache.org/POM/4.0.0" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://maven.apache.org/POM/4.0.0http://maven.apache.org/xsd/maven-4.0.0.xsd" > <-- 模型版本。maven2.0 必须是这样写,现在是maven2 唯一支持的版本 -- > <modelVersion > 4.0.0</modelVersion > <-- 公司或者组织的唯一标志,并且配置时生成的路径也是由此生成, 如com.winner.trade ,maven 会将该项目打成的jar 包放本地路径:/com /winner /trade -- > <groupId > com.winner.trade</groupId > <-- 本项目的唯一ID ,一个groupId 下面可能多个项目,就是靠artifactId 来区分的 -- > <artifactId > trade-core</artifactId > <-- 本项目目前所处的版本号 -- > <version > 1.0.0-SNAPSHOT</version > <-- 打包的机制,如pom ,jar , maven-plugin , ejb , war , ear , rar , par ,默认为jar -- > <packaging > jar</packaging > <-- 帮助定义构件输出的一些附属构件,附属构件与主构件对应,有时候需要加上classifier 才能唯一的确定该构件 不能直接定义项目的classifer ,因为附属构件不是项目直接默认生成的,而是由附加的插件帮助生成的 -- > <classifier > ...</classifier > <-- 定义本项目的依赖关系 -- > <dependencies > <-- 每个dependency 都对应这一个jar 包 -- > <dependency > <--一般情况下,maven是通过groupId、artifactId、version这三个元素值(俗称坐标)来检索该构件, 然后引入你的工程。如果别人想引用你现在开发的这个项目(前提是已开发完毕并发布到了远程仓库),-- > <--就需要在他的pom文件中新建一个dependency节点,将本项目的groupId、artifactId、version写入, maven 就会把你上传的jar 包下载到他的本地 -- > <groupId > com.winner.trade</groupId > <artifactId > trade-test</artifactId > <version > 1.0.0-SNAPSHOT</version > <-- maven 认为,程序对外部的依赖会随着程序的所处阶段和应用场景而变化,所以maven 中的依赖关系有作用域(scope )的限制。 -- > <--scope包含如下的取值:compile(编译范围)、provided(已提供范围)、runtime(运行时范围)、test(测试范围)、system(系统范围) -- > <scope > test</scope > <-- 设置指依赖是否可选,默认为false ,即子项目默认都继承: 为true ,则子项目必需显示的引入,与dependencyManagement 里定义的依赖类似 -- > <optional > false</optional > <-- 屏蔽依赖关系。 比如项目中使用的libA 依赖某个库的1.0 版,libB 依赖某个库的2.0 版,现在想统一使用2.0 版,就应该屏蔽掉对1.0 版的依赖 -- > <exclusions > <exclusion > <groupId > org.slf4j</groupId > <artifactId > slf4j-api</artifactId > </exclusion > </exclusions > </dependency > </dependencies > <-- 为pom 定义一些常量,在pom 中的其它地方可以直接引用 使用方式 如下 :${file.encoding } -- > <properties > <file.encoding > UTF-8</file.encoding > <java.source.version > 1.5</java.source.version > <java.target.version > 1.5</java.target.version > </properties > ... </project >
构建配置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 <build > <-- 产生的构件的文件名,默认值是${artifactId }- ${version }。 -- > <finalName > myPorjectName</finalName > <-- 构建产生的所有文件存放的目录,默认为${basedir }/target ,即项目根目录下的target -- > <directory > ${basedir}/target</directory > <--当项目没有规定目标(Maven2叫做阶段(phase))时的默认值, -- > <--必须跟命令行上的参数相同例如jar:jar,或者与某个阶段(phase)相同例如install、compile等 -- > <defaultGoal > install</defaultGoal > <--当filtering开关打开时,使用到的过滤器属性文件列表。 -- > <--项目配置信息中诸如${spring.version}之类的占位符会被属性文件中的实际值替换掉 -- > <filters > <filter > ../filter.properties</filter > </filters > <--项目相关的所有资源路径列表,例如和项目相关的配置文件、属性文件,这些资源被包含在最终的打包文件里。 -- > <resources > <resource > <--描述了资源的目标路径。该路径相对target /classes 目录(例如${project.build.outputDirectory })。 -- > <--举个例子,如果你想资源在特定的包里(org.apache.maven.messages),你就必须该元素设置为org /apache /maven /messages 。 -- > <--然而,如果你只是想把资源放到源码目录结构里,就不需要该配置。 -- > <targetPath > resources</targetPath > <--是否使用参数值代替参数名。参数值取自properties元素或者文件里配置的属性,文件在filters元素里列出。 -- > <filtering > true</filtering > <--描述存放资源的目录,该路径相对POM路径 -- > <directory > src/main/resources</directory > <--包含的模式列表 -- > <includes > <include > **/*.properties</include > <include > **/*.xml</include > </includes > <--排除的模式列表 如果<include>与<exclude>划定的范围存在冲突,以<exclude>为准 --> <excludes > <exclude > jdbc.properties</exclude > </excludes > </resource > </resources > <--单元测试相关的所有资源路径,配制方法与resources类似 -- > <testResources > <testResource > <targetPath /> <filtering /> <directory /> <includes /> <excludes /> </testResource > </testResources > <--项目源码目录,当构建项目的时候,构建系统会编译目录里的源码。该路径是相对于pom.xml的相对路径。 -- > <sourceDirectory > ${basedir}\src\main\java</sourceDirectory > <--项目脚本源码目录,该目录和源码目录不同, <-- 绝大多数情况下,该目录下的内容会被拷贝到输出目录(因为脚本是被解释的,而不是被编译的)。 --> <scriptSourceDirectory > ${basedir}\src\main\scripts </scriptSourceDirectory > <--项目单元测试使用的源码目录,当测试项目的时候,构建系统会编译目录里的源码。该路径是相对于pom.xml的相对路径。 -- > <testSourceDirectory > ${basedir}\src\test\java</testSourceDirectory > <--被编译过的应用程序class文件存放的目录。 -- > <outputDirectory > ${basedir}\target\classes</outputDirectory > <--被编译过的测试class文件存放的目录。 -- > <testOutputDirectory > ${basedir}\target\test-classes </testOutputDirectory > <--项目的一系列构建扩展,它们是一系列build过程中要使用的产品,会包含在running bulid ‘s classpath 里面。 -- > <--他们可以开启extensions,也可以通过提供条件来激活plugins。 -- > <--简单来讲,extensions是在build过程被激活的产品 -- > <extensions > <--例如,通常情况下,程序开发完成后部署到线上Linux服务器,可能需要经历打包、 -- > <--将包文件传到服务器、SSH连上服务器、敲命令启动程序等一系列繁琐的步骤。 -- > <--实际上这些步骤都可以通过Maven的一个插件 wagon-maven-plugin 来自动完成 -- > <--下面的扩展插件wagon-ssh用于通过SSH的方式连接远程服务器, -- > <--类似的还有支持ftp方式的wagon-ftp插件 -- > <extension > <groupId > org.apache.maven.wagon</groupId > <artifactId > wagon-ssh</artifactId > <version > 2.8</version > </extension > </extensions > <--使用的插件列表 。 -- > <plugins > <plugin > <groupId > </groupId > <artifactId > maven-assembly-plugin</artifactId > <version > 2.5.5</version > <--在构建生命周期中执行一组目标的配置。每个目标可能有不同的配置。 -- > <executions > <execution > <--执行目标的标识符,用于标识构建过程中的目标,或者匹配继承过程中需要合并的执行目标 -- > <id > assembly</id > <--绑定了目标的构建生命周期阶段,如果省略,目标会被绑定到源数据里配置的默认阶段 -- > <phase > package</phase > <--配置的执行目标 -- > <goals > <goal > single</goal > </goals > <--配置是否被传播到子POM -- > <inherited > false</inherited > </execution > </executions > <--作为DOM对象的配置,配置项因插件而异 -- > <configuration > <finalName > ${finalName}</finalName > <appendAssemblyId > false</appendAssemblyId > <descriptor > assembly.xml</descriptor > </configuration > <--是否从该插件下载Maven扩展(例如打包和类型处理器), -- > <--由于性能原因,只有在真需要下载时,该元素才被设置成true。 -- > <extensions > false</extensions > <--项目引入插件所需要的额外依赖 -- > <dependencies > <dependency > ...</dependency > </dependencies > <--任何配置是否被传播到子项目 -- > <inherited > true</inherited > </plugin > </plugins > <--主要定义插件的共同元素、扩展元素集合,类似于dependencyManagement, -- > <--所有继承于此项目的子项目都能使用。该插件配置项直到被引用时才会被解析或绑定到生命周期。 -- > <--给定插件的任何本地配置都会覆盖这里的配置 -- > <pluginManagement > <plugins > ...</plugins > </pluginManagement > </build >
分发配置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 <--项目分发信息,在执行mvn deploy 后表示要发布的位置。 -- > <--有了这些信息就可以把网站部署到远程服务器或者把构件部署到远程仓库。 -- > <distributionManagement > <--部署项目产生的构件到远程仓库需要的信息 -- > <repository > <--是分配给快照一个唯一的版本号(由时间戳和构建流水号),还是每次都使用相同的版本号 -- > <--参见repositories /repository 元素 -- > <uniqueVersion > true</uniqueVersion > <id > repo-id </id > <name > repo-name</name > <url > file://${basedir}/target/deploy </url > <layout /> </repository > <--构件的快照部署到哪里,如果没有配置该元素,默认部署到repository元素配置的仓库 -- > <snapshotRepository > <uniqueVersion /> <id /> <name /> <url /> <layout /> </snapshotRepository > <--部署项目的网站需要的信息 -- > <site > <--部署位置的唯一标识符,用来匹配站点和settings.xml文件里的配置 -- > <id > site-id </id > <--部署位置的名称 -- > <name > site-name</name > <--部署位置的URL,按protocol: //hostname /path 形式 -- > <url > scp://svn.baidu.com/banseon:/var/www/localhost/banseon-web </url > </site > <--项目下载页面的URL。如果没有该元素,用户应该参考主页。 -- > <--使用该元素的原因是:帮助定位那些不在仓库里的构件(由于license限制)。 -- > <downloadUrl /> <--如果构件有了新的groupID和artifact ID (构件移到了新的位置),这里列出构件的重定位信息。 -- > <relocation > <--构件新的group ID -- > <groupId /> <--构件新的artifact ID -- > <artifactId /> <--构件新的版本号 -- > <version /> <--显示给用户的,关于移动的额外信息,例如原因。 -- > <message /> </relocation > <--给出该构件在远程仓库的状态。不得在本地项目中设置该元素,因为这是工具自动更新的。 -- > <--有效的值有:none(默认),converted(仓库管理员从Maven 1 POM 转换过来), -- > <--partner(直接从伙伴Maven 2 仓库同步过来),deployed (从Maven 2 实例部署),verified (被核实时正确的和最终的)。 -- > <status /> </distributionManagement >
仓库配置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 <--发现依赖和扩展的远程仓库列表。 -- > <repositories > <--包含需要连接到远程仓库的信息 -- > <repository > <--如何处理远程仓库里发布版本的下载 -- > <releases > <--true或者false表示该仓库是否为下载某种类型构件(发布版,快照版)开启。 -- > <enabled /> <--该元素指定更新发生的频率。Maven会比较本地POM和远程POM的时间戳。 -- > <--这里的选项是:always(一直),daily(默认,每日), -- > <--interval:X(这里X是以分钟为单位的时间间隔),或者never(从不)。 -- > <updatePolicy /> <--当Maven验证构件校验文件失败时该怎么做: -- > <--ignore(忽略),fail(失败),或者warn(警告)。 -- > <checksumPolicy /> </releases > <--如何处理远程仓库里快照版本的下载。有了releases和snapshots这两组配置, -- > <--POM就可以在每个单独的仓库中,为每种类型的构件采取不同的策略。 -- > <--例如,可能有人会决定只为开发目的开启对快照版本下载的支持 -- > <snapshots > <enabled /> <updatePolicy /> <checksumPolicy /> </snapshots > <--远程仓库唯一标识符。可以用来匹配在settings.xml文件里配置的远程仓库 -- > <id > repo-id </id > <--远程仓库名称 -- > <name > repo-name</name > <--远程仓库URL,按protocol: //hostname /path 形式 -- > <url > http://192.168.1.169:9999/repository/ </url > <--用于定位和排序构件的仓库布局类型-可以是default(默认)或者legacy(遗留)。 -- > <--Maven 2 为其仓库提供了一个默认的布局; -- > <--然而,Maven1.x有一种不同的布局。 -- > <--我们可以使用该元素指定布局是default(默认)还是legacy(遗留)。 -- > <layout > default</layout > </repository > </repositories > <--发现插件的远程仓库列表,这些插件用于构建和报表 -- > <pluginRepositories > <--包含需要连接到远程插件仓库的信息.参见repositories /repository 元素 -- > <pluginRepository /> </pluginRepositories >
profile 配置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 <--在列的项目构建profile,如果被激活,会修改构建处理 -- > <profiles > <--根据环境参数或命令行参数激活某个构建处理 -- > <profile > <--自动触发profile的条件逻辑。Activation是profile的开启钥匙。 -- > <activation > <--profile默认是否激活的标识 -- > <activeByDefault > false</activeByDefault > <--activation有一个内建的java版本检测,如果检测到jdk版本与期待的一样,profile被激活。 -- > <jdk > 1.7</jdk > <--当匹配的操作系统属性被检测到,profile被激活。os元素可以定义一些操作系统相关的属性。 -- > <os > <--激活profile的操作系统的名字 -- > <name > Windows XP</name > <--激活profile的操作系统所属家族(如 'windows ') -- > <family > Windows</family > <--激活profile的操作系统体系结构 -- > <arch > x86</arch > <--激活profile的操作系统版本 -- > <version > 5.1.2600</version > </os > <--如果Maven检测到某一个属性(其值可以在POM中通过${名称}引用),其拥有对应的名称和值,Profile就会被激活。 -- > <-- 如果值字段是空的,那么存在属性名称字段就会激活profile ,否则按区分大小写方式匹配属性值字段 -- > <property > <--激活profile的属性的名称 -- > <name > mavenVersion</name > <--激活profile的属性的值 -- > <value > 2.0.3</value > </property > <--提供一个文件名,通过检测该文件的存在或不存在来激活profile。missing检查文件是否存在,如果不存在则激活profile。 -- > <--另一方面,exists则会检查文件是否存在,如果存在则激活profile。 -- > <file > <--如果指定的文件存在,则激活profile。 -- > <exists > /usr/local/hudson/hudson-home/jobs/maven-guide-zh-to-production/workspace/</exists > <--如果指定的文件不存在,则激活profile。 -- > <missing > /usr/local/hudson/hudson-home/jobs/maven-guide-zh-to-production/workspace/</missing > </file > </activation > <id /> <build /> <modules /> <repositories /> <pluginRepositories /> <dependencies /> <reporting /> <dependencyManagement /> <distributionManagement /> <properties /> </profile >
profile配置项在setting.xml中也有,是pom.xml中profile元素的裁剪版本,包含了id,activation, repositories, pluginRepositories和 properties元素。这里的profile元素只包含这五个子元素是因为setting.xml只关心构建系统这个整体(这正是settings.xml文件的角色定位),而非单独的项目对象模型设置。一个settings中的profile被激活,它的值会覆盖任何其它定义在POM中或者profile.xml中的带有相同id的profile 。
pom.xml中的profile可以看做pom.xml的副本,拥有与pom.xm** l相同的子元素与配置方法**。它包含可选的activation(profile的触发器)和一系列的changes。例如test过程可能会指向不同的数据库(相对最终的deployment)或者不同的dependencies或者不同的repositories,并且是根据不同的JDK来改变的。只需要其中一个成立就可以激活profile,如果第一个条件满足了,那么后面就不会在进行匹配。
约定大于配置 Maven 默认约定了一套目录结构,在通过maven创建了项目以后,项目的目录结构就是以这套目录结构作为模板创建的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 ${basedir} |-- pom.xml |-- src | |-- main | | `-- java | | `-- resources | | `-- filters | `-- test | | `-- java | | `-- resources | | `-- filters | `-- it | `-- assembly | `-- site `-- LICENSE.txt `-- NOTICE.txt `-- README.txt
在根目录下,主要有两个目录,分别是src和target目录 ,除此之外,还会有一些版本控制系统的元数据文件,包括git的.gitignore或者svn的.svn。如果一个项目是由多个子项目组成的,那么在该项目的根目录下,还会包含它的子项目的目录。子项目的目录布局,默认也是和上面列出的目录结构一致的。target目录,这个目录只要用于存放项目构建的输出文件,比如class文件以及打包后的包文件等 。在src目录下,包含了项目所有的源代码和资源文件,以及其他项目相关的文件。在该目录的main目录下,包含了构建该项目的artifact(可以理解为时项目的程序部分)所需的代码和资源,而test目录包含了测试相关的代码和资源文件。
版本规范
SNAPSHOT 如果一个版本包含字符串”SNAPSHOT”,Maven就会在安装或发布这个组件的时候将该符号展开为一个日期和时间值,转换为UTC时间。例如,”1.0-SNAPSHOT”会在2010年5月5日下午2点10分发布时候变成1.0-20100505-141000-1。 这个词只能用于开发过程中,因为一般来说,项目组都会频繁发布一些版本,最后实际发布的时候,会在这些snapshot版本中寻找一个稳定的,用于正式发 布,比如1.4版本发布之前,就会有一系列的1.4-SNAPSHOT,而实际发布的1.4,也是从中拿出来的一个稳定版。
LATEST 指某个特定构件的最新发布,这个发布可能是一个发布版,也可能是一个snapshot版,具体看哪个时间最后。
RELEASE 指最后一个发布版。
Maven 命令 常用命令
Maven命令列表
目标
mvn –version
显示版本信息
mvn clean
清理项目生产的临时文件,一般是模块下的target目录
mvn compile
编译源代码,一般编译模块下的src/main/Java目录
mvn package
项目打包工具,会在模块下的target目录生成jar或war等文件
mvn test
测试命令,或执行src/test/java/下junit的测试用例.
mvn install
将打包的jar/war文件复制到你的本地仓库中,供其他模块使用
mvn deploy
将打包的文件发布到远程参考,提供其他人员进行下载依赖
mvn site
生成项目相关信息的网站
mvn eclipse:eclipse
将项目转化为Eclipse项目
mvn dependency:tree
打印出项目的整个依赖树
mvn archetype:generate
创建Maven的普通java项目
mvn tomcat:run
在tomcat容器中运行web应用
mvn jetty:run
调用 Jetty 插件的 Run 目标在 Jetty Servlet 容器中启动 web 应用
命令参数 -D 传入属性参数 eg:
1 2 3 mvn package -Dmaven.test.skip=true --------------------------------- 以“-D”开头,将“maven.test.skip”的值设为“true”,就是告诉maven打包的时候跳过单元测试。同理,“mvn deploy -Dmaven.test.skip=true”代表部署项目并跳过单元测试。
-P 使用指定的Profile配置
-e 显示maven运行出错的信息
-o 离线执行命令,即不去远程仓库更新包
-X 显示maven允许的debug信息
-U 强制去远程更新snapshot的插件或依赖,默认每天只更新一次
仓库 中央仓库、中央仓库的镜像仓库、其他公共仓库、私服都属于远程仓库的范畴。
如果maven没有在本地仓库找到想要的东西,就会自动去配置文件中指定的远程仓库寻找,找到后将它下载到本地仓库。如果连远程仓库都找不到想要的东西,肯定是配置写错了,就会报错。
本地仓库 仓库分为本地仓库和远程仓库,是依赖和插件的存储的地方,本地仓库是在自己电脑上,默认位置是${user.home}/.m2/repository,在pom.xml中声明之后,maven会首先在本地仓库中找,找到了,自动引入工程的依赖lib库即可。找不到需要去远程仓库查找。
远程仓库 远程仓库,先从最核心的中央仓库开始,中央仓库是默认的远程仓库,maven在安装的时候,自带的默认中央仓库地址为http://repo1.maven.org/maven2/,此仓库由Maven社区管理,包含了绝大多数流行的开源Java构件,以及源码、作者信息、SCM、信息、许可证信息等。一般来说,简单的Java项目依赖的构件都可以在这里下载到。Maven社区提供了一个中央仓库的搜索地址:http://search.maven.org/#browse,可以查询到所有可用的库文件。
除了中央仓库,还有其它很多公共的远程仓库,如中央仓库的镜像仓库。全世界都从中央仓库请求资源,中央仓库扛不住啊,所以在世界各地还有很多中央仓库的镜像仓库。镜像仓库可以理解为仓库的副本,会从原仓库定期更新资源,以保持与原仓库的一致性。从仓库中可以找到的构件,从镜像仓库中也可以找到,直接访问镜像仓库,更快更稳定。
除此之外,还有很多各具特色的公共仓库,如果需要都可以在网上找到,比如Apache Snapshots仓库,包含了来自于Apache软件基金会的快照版本。
一般来讲,公司都会通过自己的私有服务器在局域网内架设一个仓库代理。私服可以看作一种特殊的远程仓库,代理广域网上的远程仓库,供局域网内的Maven用户使用。当Maven需要下载构件的时候,先从私服请求,如果私服上不存在该构件,则从外部的远程仓库下载,缓存在私服上之后,再为Maven的下载请求提供服务。
Maven私服有很多好处 :
可以把公司的私有jar包,以及无法从外部仓库下载到的构件上传到私服上,供公司内部使用;
节省自己的外网带宽:减少重复请求造成的外网带宽消耗;
加速Maven构建:如果项目配置了很多外部远程仓库的时候,构建速度就会大大降低;
提高稳定性,增强控制:Internet不稳定的时候,maven构建也会变的不稳定,一些私服软件还提供了其他的功能
当前主流的maven私服有Apache的Archiva、JFrog的Artifactory以及Sonatype的Nexus。
仓库配置 仓库配置要做两件事,一是告诉maven你的本地仓库在哪里,二是你的远程仓库在哪里。
setting.xml的第一个节点<localRepository>
就是配置本地仓库的地方,
远程仓库比较复杂,因为会涉及很多附属特性。以Nexus为例,至于Nexus怎么部署,怎么维护仓库,作为开发人员是不需要关心的,只需要把Nexus私服的局域网地址写入maven的本地配置文件即可。具体的配置方法如下
1 设置镜像
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <mirrors > <mirror > <-- 该镜像的唯一标识符。id 用来区分不同的mirror 元素。 -- > <id > nexus</id > <-- 镜像名,起注解作用,应做到见文知意。可以不配置 -- > <name > Human Readable Name </name > <-- 所有仓库的构件都要从镜像下载 -- > <mirrorOf > *</mirrorOf > <-- 私服的局域网地址-- > <url > http://192.168.0.1:8081/nexus/content/groups/public/</url > </mirror > </mirrors > ----------------------- 节点<mirrors > 下面可以配置多个镜像,<mirrorOf > 用于指明是哪个仓库的镜像,上例中使用通配符“*”表明该私服是所有仓库的镜像,不管本地使用了多少种远程仓库,需要下载构件时都会从私服请求。 如果只想将私服设置成某一个远程仓库的镜像,使用<mirrorOf > 指定该远程仓库的ID即可。
2设置远程仓库
远程仓库的设置在<profile>
节点下面:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 <repositories > <repository > <-- 仓库唯一标识 -- > <id > repoId </id > <-- 远程仓库名称 -- > <name > repoName</name > <-- 远程仓库URL ,如果该仓库配置了镜像,这里的URL 就没有意义了,因为任何下载请求都会交由镜像仓库处理,前提是镜像(也就是设置好的私服)需要确保该远程仓库里的任何构件都能通过它下载到 -- > <url > http://……</url > <-- 如何处理远程仓库里发布版本的下载 -- > <releases > <-- true 或者false 表示该仓库是否为下载某种类型构件(发布版,快照版)开启。 -- > <enabled > false</enabled > <-- 该元素指定更新发生的频率。Maven 会比较本地POM 和远程POM 的时间戳。这里的选项是:-- > <-- always (一直),daily (默认,每日),interval :X (这里X 是以分钟为单位的时间间隔),或者never (从不)。 -- > <updatePolicy > always</updatePolicy > <-- 当Maven 验证构件校验文件失败时该怎么做:-- > <-- ignore (忽略),fail (失败),或者warn (警告)。 -- > <checksumPolicy > warn</checksumPolicy > </releases > <-- 如何处理远程仓库里快照版本的下载,与发布版的配置类似 -- > <snapshots > <enabled /> <updatePolicy /> <checksumPolicy /> </snapshots > </repository > </repositories >
依赖管理 项目的依赖关系主要分为三种,依赖,继承,聚合
依赖关系是最常用的一种,就是你的项目需要依赖其他项目,比如Apache-common包,Spring包等等。
1 2 3 4 5 6 7 8 <dependency > <groupId > junit</groupId > <artifactId > junit</artifactId > <version > 4.11</version > <scope > test</scope > <type > jar</ type > <optional > true</ optional > </dependency >
任意一个外部依赖说明包含如下几个要素:groupId, artifactId, version, scope, type, optional 。其中前3个是必须的。
继承就是避免重复,maven的继承也是这样,虽然前面进行了介绍,但是为了进一步强调,还是重复一下,它还有一个好处就是让项目更加安全。项目之间存在上下级关系时就属于继承关系。
父项目的配置如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 <project > <modelVersion > 4.0.0</modelVersion > <groupId > org.clf.parent</groupId > <artifactId > my-parent</artifactId > <version > 2.0</version > <packaging > pom</packaging > <-- 该节点下的依赖会被子项目自动全部继承 -- > <dependencies > <dependency > <groupId > org.slf4j</groupId > <artifactId > slf4j-api</artifactId > <version > 1.7.7</version > <type > jar</type > <scope > compile</scope > </dependency > </dependencies > <dependencyManagement > <-- 该节点下的依赖关系只是为了统一版本号,不会被子项目自动继承,-- > <-- 除非子项目主动引用,好处是子项目可以不用写版本号 -- > <dependencies > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-orm</artifactId > <version > 4.2.5.RELEASE</version > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-web</artifactId > <version > 4.2.5.RELEASE</version > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-context-support</artifactId > <version > 4.2.5.RELEASE</version > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-beans</artifactId > <version > 4.2.5.RELEASE</version > </dependency > </dependencies > </dependencyManagement > <-- 这个元素和dependencyManagement 相类似,它是用来进行插件管理的-- > <pluginManagement > ...... </pluginManagement > </project >
Maven 使用dependencyManagement 元素来提供了一种管理依赖版本号的方式 。通常会在一个组织或者项目的最顶层的父POM 中看到dependencyManagement 元素。使用pom.xml 中的dependencyManagement 元素能让所有在子项目中引用一个依赖而不用显式的列出版本号。Maven 会沿着父子层次向上走,直到找到一个拥有dependencyManagement元素的项目,然后它就会使用在这个dependencyManagement 元素中指定的版本号。
父项目在dependencies声明的依赖,子项目会从全部自动地继承 。而父项目在dependencyManagement里只是声明依赖,并不实现引入 ,因此子项目需要显示的声明需要用的依赖。如果不在子项目中声明依赖,是不会从父项目中继承下来的;只有在子项目中写了该依赖项,并且没有指定具体版本,才会从父项目中继承该项,并且version和scope都读取自父pom另外如果子项目中指定了版本号,那么会使用子项目中指定的jar版本。
如果某个项目需要继承该父项目,基础配置应该这样
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 <project > <modelVersion > 4.0.0</modelVersion > <groupId > org.clf.parent.son</groupId > <artifactId > my-son</artifactId > <version > 1.0</version > <-- 声明将父项目的坐标 -- > <parent > <groupId > org.clf.parent</groupId > <artifactId > my-parent</artifactId > <version > 2.0</version > <-- 父项目的pom.xml 文件的相对路径。相对路径允许你选择一个不同的路径。 -- > <-- 默认值是.. /pom.xml 。Maven 首先在构建当前项目的地方寻找父项目的pom , -- > <-- 其次在文件系统的这个位置(relativePath 位置), -- > <-- 然后在本地仓库,最后在远程仓库寻找父项目的pom 。 -- > <relativePath > ../parent-project/pom.xml</relativePath > </parent > <-- 声明父项目dependencyManagement 的依赖,不用写版本号 -- > <dependencies > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-web</artifactId > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-beans</artifactId > </dependency > </dependencies > </project >
maven的多模块管理也是非常强大的。一般来说,maven要求同一个工程的所有模块都放置到同一个目录下,每一个子目录代表一个模块,比如
1 2 3 4 5 6 总项目/ pom.xml 总项目的pom配置文件 子模块1/ pom.xml 子模块1的pom文件 子模块2/ pom.xml子模块2的pom文件
总项目配置如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 <project > <modelVersion > 4.0.0</modelVersion > <groupId > org.clf.parent</groupId > <artifactId > my-parent</artifactId > <version > 2.0</version > <-- 打包类型必须为pom -- > <packaging > pom</packaging > <-- 声明了该项目的直接子模块 -- > <modules > <-- 这里配置的不是artifactId ,而是这个模块的目录名称-- > <module > module-1</module > <module > module-2</module > <module > module-3</module > </modules > <-- 聚合也属于父子关系,总项目中的dependencies 与dependencyManagement 、pluginManagement 用法与继承关系类似 -- > <dependencies > ...... </dependencies > <dependencyManagement > ...... </dependencyManagement > <pluginManagement > ...... </pluginManagement > </project >
子模块的配置如下:
1 2 3 4 5 6 7 8 9 10 11 12 <project > <modelVersion > 4.0.0</modelVersion > <groupId > org.clf.parent.son</groupId > <artifactId > my-son</artifactId > <version > 1.0</version > <-- 声明将父项目的坐标 -- > <parent > <groupId > org.clf.parent</groupId > <artifactId > my-parent</artifactId > <version > 2.0</version > </parent > </project >
首先,继承与聚合都属于父子关系,并且,聚合 POM与继承关系中的父POM的packaging都是pom。 不同的是,对于聚合模块来说,它知道有哪些被聚合的模块,但那些被聚合的模块不知道这个聚合模块的存在。对于继承关系的父 POM来说,它不知道有哪些子模块继承与它,但那些子模块都必须知道自己的父 POM是什么。 在实际项目中,一个 POM往往既是聚合POM,又是父 POM,它继承了某个项目,本身包含几个子模块,同时肯定会存在普通的依赖关系,就是说,依赖、继承、聚合这三种关系是并存的。